home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-12-12 | 13.6 KB | 683 lines | [TEXT/KAHL] |
- /***
- *
- * Interpreter.cp - bytecode interpreter
- *
- * Original code: Copyright (c) 1991, by David Michael Betz. All rights reserved
- * Modifications and additions: Copyright © by Christopher E. Hyde, 1995
- *
- ***/
-
- #include "Bob.h"
-
- #define qTimeOpCodes 0
- #if qDebug
- #define qDebugInterp 0
- #else
- #define qDebugInterp 0
- #endif
-
- #define IsZero(x) ((x)->fType == tInteger && (x)->fInt == 0)
- #define IsTrue(x) ((x)->fType != tNil && !IsZero(x))
-
- // Argument check macros
- #define CheckType(o,t) { if IsNotType(o, t) { gPC = pc; BadType(o,t); } }
- #define Check0(t) { if IsNotType(0, t) Arg0Not(t); }
- #define CheckInt0() { if IsNotType(0, tInteger) { gPC = pc; Arg0NotInt(); } }
- #define CheckInt1() { if IsNotType(1, tInteger) { gPC = pc; Arg1NotInt(); } }
-
- // Global variables
- CodePtr cbase; // the base code address
- CodePtr gPC; // global copy of the program counter
- Vector code; // the current code vector
- Value stkbase; // the runtime stack
- Value stktop; // the top of the stack
- Value sp; // the stack pointer
- Value fp; // the frame pointer
-
- // External variables
- extern TValue symbols;
-
- // Forward declarations
- static void Interpret (int);
- static CodePtr OpCall (CodePtr pc);
- static CodePtr OpReturn (CodePtr pc);
- static CodePtr OpSend (CodePtr pc);
- static CodePtr OpVRef (CodePtr pc);
- static CodePtr OpVSet (CodePtr pc);
- static CodePtr OpAdd (CodePtr pc);
- /*static*/ KStr TypeName (int type);
-
- char pSubsErr[] = "subscript out of bounds: %d";
-
-
- #if 0
- static int
- Fib (int n)
- {
- return (n == 1 || n == 0)
- ? 1
- : Fib(n - 2) + Fib(n - 1);
- }
-
-
- static void
- PrintFib (int n)
- {
- UInt32 tEnd, tStart = TickCount();
- int result = Fib(n);
-
- tEnd = TickCount() - tStart;
- PrintErrF("C - Fib(%d) = %d\r", n, result);
- PrintErrF(" Executed in %d/60 seconds\r\r", tEnd);
- }
- #endif
-
-
- #if qTimeOpCodes
- static UInt32 pTickCount;
- #endif
-
-
- // Execute a bytecode function
- void
- Execute (KStr name)
- {
- // lookup the symbol
- Entry sym = FindEntry(&symbols, name);
-
- if (sym != nil) {
- // setup the stack
- sp = fp = stktop;
- *--sp = sym->fValue;
-
- if (sp->fType == tByteCode) {
- #if 0
- PrintFib(20);
-
- UInt32 t = TickCount();
- Interpret(0);
- PrintErrF("Executed in %d/60 seconds\r\r", TickCount() - t);
- #else
- #if qTimeOpCodes
- pTickCount = TickCount();
- #endif
- Interpret(0);
- #endif
- return;
- }
- }
- // Error("Cannot find the function ‘main’")
- Fail(errNoMainFunction);
- }
-
-
- #define qHack68K
- #if defined(qHack68K) && !__powerc
-
- extern "C" {
- #pragma parameter __A0 GetPC
- extern pascal UInt16* GetPC (void) = { 0x41FA, 0 }; // LEA *,A0
- }
-
- enum {
- cLINK = 0x4E56, // LINK A6,#?
- cMOVEM = 0x48E7, // MOVEM.L ?,-(A7)
- cMOVEQ = 0x7000, // MOVEQ #0,D0
- cMOVEB = 0x1001, // MOVE.B D1,D0
- cMOVEQ_D1 = 0x72,
- cCMPL = 0xB081, // CMP.L D1,D0
- cBEQ = 0x6700
- };
-
-
- // NOTE: For this hack to work
- void
- HackInterpretFn (void)
- {
- UInt16* pc = GetPC();
-
- // First skip to start of next function
- while (pc[0] != cLINK || pc[2] != cMOVEM)
- #if qDebugInterp
- if (long(++pc) - long(GetPC()) > 750)
- Error("HackFn: Can’t find Interpret fn\r");
- #else
- ++pc;
- #endif
-
- // Search for start of switch statement
- while (pc[0] != cMOVEQ || pc[1] != cMOVEB)
- #if qDebugInterp
- if (long(++pc) - long(GetPC()) > 999)
- Error("HackFn: Can’t find switch statement\r");
- #else
- ++pc;
- #endif
- pc += 2;
-
- UInt16* base = pc;
- UInt16 offsets[_opLast + 1];
-
- for (int i = 0; i <= _opLast; ++i)
- offsets[i] = 0;
-
- // Parse each:
- // 72?? MOVEQ #??,D1
- // B081 CMP.L D1,D0
- // 6700 ???? BEQ *+????
-
- while ((pc[0] >> 8) == cMOVEQ_D1 && pc[1] == cCMPL && pc[2] == cBEQ) {
- UInt8 opcode = pc[0];
- #if qDebugInterp
- if (opcode < _opFirst || opcode > _opLast)
- Error("HackFn: Bad opcode (0x%X)\r", opcode);
- if (offsets[opcode] != 0)
- Error("HackFn: Repeat opcode (0x%X)\r", opcode);
- #endif
- offsets[opcode] = (long(pc) + 6) - (long(base) + 8) + pc[3];
- pc += 4;
- }
-
- pc = base; // Save a copy of base
-
- // Now build the new switch instructions
- *pc++ = 0x41FA; // LEA *+8,A0
- *pc++ = 0x0006;
- *pc++ = 0xD0F0; // ADD.W 0(A0,D0.L*2),A0
- *pc++ = 0x0A00;
- *pc++ = 0x4ED0; // JMP (A0)
-
- for (i = _opFirst; i <= _opLast; ++i) {
- #if qDebugInterp
- if (offsets[i] == 0)
- Error("HackFn: Undefined opcode (0x%X)\r", i);
- #endif
- *pc++ = offsets[i];
- }
-
- FlushCodeCacheRange(base, long(pc) - long(base));
- //Debugger();
- }
- #endif // qHack68K
-
-
- #define DoBranch() pc = cbase + (*(CWord*) pc)
- #define _IntOp(X, OP) { \
- CheckInt0(); CheckInt1(); \
- X (sp[1].fInt OP sp->fInt); \
- ++sp; \
- }
- #define IntEqOp(op) _IntOp((void), op)
- #define RelOp(rel) { \
- _IntOp(n =, rel); \
- set_integer(sp, n ? true : false); \
- }
- enum {
- kCommandKey = 0x37,
- kFullStopKey = 0x2F
- };
- #define TestKey(n) (theKeys[(n) / 32] & (1 << ((n) % 32)))
-
-
- // Interpret bytecode instructions
- static void
- Interpret (int argc)
- {
- CodePtr pc; // the program counter
-
- // make a dummy call frame
- check(3);
- code = sp[argc].fVec;
- push_integer(argc); // argument count
- push_integer(stktop - fp); // old fp
- push_integer(0); // old pc
- cbase = pc = (CodePtr) code->fData[kIByteCodes].fStr->fData;
- fp = sp;
-
- UInt16 nextCmdStop = 0;
- for (;;) { // Execute each instruction
- if (Opt(TraceExec)) {
- DumpInstruction(code, pc - cbase);
- nextCmdStop = 1;
- }
- if (--nextCmdStop == 0) { // Minimize the overhead per instruction
- KeyMap theKeys;
- GetKeys(theKeys);
- #if qTimeOpCodes
- static long testCount = 0;
- ++testCount;
- if (TestKey(kFullStopKey) && TestKey(kCommandKey))
- Error("errUserInterrupt: %d tests in %d ticks",
- testCount, TickCount() - pTickCount);
- #else
- if (TestKey(kFullStopKey) && TestKey(kCommandKey))
- Fail(errUserInterrupt);
- #endif
- }
- switch (*pc++) {
- case opCALL: pc = OpCall(pc); break;
- case opRTS: pc = OpReturn(pc);
- if (pc == nil) return;
- break;
- case opSEND: pc = OpSend(pc); break;
- case opADD: pc = OpAdd(pc); break;
- case opVREF: pc = OpVRef(pc); break;
- case opVSET: pc = OpVSet(pc); break;
- case opREF:
- *sp = code->fData[*pc++].fVar->fValue;
- break;
- case opSET:
- code->fData[*pc++].fVar->fValue = *sp;
- break;
- case opMREF:
- Object obj = fp[fp[2].fInt + 2].fObject;
- *sp = obj->fMembers[*pc++];
- break;
- case opMSET:
- obj = fp[fp[2].fInt + 2].fObject;
- obj->fMembers[*pc++] = *sp;
- break;
- case opAREF:
- int n = *pc++;
- if (n >= fp[2].fInt)
- Error("Too few arguments");
- *sp = fp[n + 3];
- break;
- case opASET:
- n = *pc++;
- if (n >= fp[2].fInt)
- Error("Too few arguments");
- fp[n + 3] = *sp;
- break;
- case opTREF:
- n = *pc++;
- *sp = fp[-n - 1];
- break;
- case opTSET:
- n = *pc++;
- fp[-n - 1] = *sp;
- break;
- case opTSPC:
- n = *pc++;
- check(n);
- while (--n >= 0) {
- --sp;
- set_nil(sp);
- }
- break;
- case opBRT:
- if (IsTrue(sp))
- DoBranch();
- else
- pc += 2;
- break;
- case opBRF:
- if (IsTrue(sp))
- pc += 2;
- else
- DoBranch();
- break;
- case opBR:
- DoBranch();
- break;
- case opNIL:
- set_nil(sp);
- break;
- case opPUSH:
- check(1);
- push_integer(false);
- break;
- case opNOT:
- set_integer(sp, !IsTrue(sp));
- break;
- case opNEG:
- CheckInt0();
- sp->fInt = -sp->fInt;
- break;
- case opSUB: IntEqOp(-=); break;
- case opMUL: IntEqOp(*=); break;
- case opDIV:
- CheckInt0();
- CheckInt1();
- if (sp->fInt != 0)
- sp[1].fInt /= sp->fInt;
- else
- sp[1].fInt = 0;
- ++sp;
- break;
- case opREM:
- CheckInt0();
- CheckInt1();
- if (sp->fInt != 0)
- sp[1].fInt %= sp->fInt;
- else
- sp[1].fInt = 0;
- ++sp;
- break;
- case opINC:
- CheckInt0();
- ++sp->fInt;
- break;
- case opDEC:
- CheckInt0();
- --sp->fInt;
- break;
- case opBAND: IntEqOp(&=); break;
- case opBOR: IntEqOp(|=); break;
- case opXOR: IntEqOp(^=); break;
- case opBNOT:
- CheckInt0();
- sp->fInt = ~sp->fInt;
- break;
- case opSHL:
- #if 1
- switch (sp[1].fType) {
- case tInteger:
- CheckInt0();
- sp[1].fInt <<= sp->fInt;
- break;
- case tStream:
- Print(&sp[1], false, &sp[0]);
- break;
- default:
- break;
- }
- ++sp;
- #else
- if (IsType(1, tStream)) {
- Print(&sp[1], false, &sp[0]);
- ++sp;
- } else
- IntEqOp(<<=);
- #endif
- break;
- case opSHR: IntEqOp(>>=); break;
- case opLT: RelOp(<); break;
- case opLE: RelOp(<=); break;
- case opEQ: RelOp(==); break;
- case opNE: RelOp(!=); break;
- case opGE: RelOp(>=); break;
- case opGT: RelOp(>); break;
- case opLIT:
- *sp = code->fData[*pc++];
- break;
- case opDUP2:
- check(2);
- sp -= 2;
- *sp = sp[2];
- sp[1] = sp[3];
- break;
- case opNEW:
- CheckType(0, tClass);
- gPC = pc; // in case GC() is called
- set_object(sp, NewObject(sp));
- pc = gPC;
- break;
- case opINT:
- // set_integer(sp, *((CWord*) pc)++);
- set_integer(sp, *(CWord*) pc);
- pc += sizeof(CWord);
- break;
- default:
- Error("Bad opcode %02X", pc[-1]);
- break;
- }
- }
- }
-
-
- // CALL opcode handler
- static CodePtr
- OpCall (CodePtr pc)
- {
- register int n = *pc++; // get argument count
-
- switch (sp[n].fType) {
- case tCode:
- gPC = pc;
- (*sp[n].fCode)(n);
- break;
- case tByteCode:
- check(3);
- code = sp[n].fVec;
- push_integer(n); // argument count
- push_integer(stktop - fp); // old fp
- push_integer(pc - cbase); // old pc
- cbase = pc = (CodePtr) code->fData[0].fStr->fData;
- fp = sp;
- break;
- default:
- Error("Call to non-procedure, Type %s", TypeName(sp[n].fType));
- break;
- }
- return pc;
- }
-
-
- // RTS opcode handler
- static CodePtr
- OpReturn (CodePtr pc)
- {
- TValue val = *sp;
-
- sp = fp;
- int pcoff = fp[0].fInt;
- int n = fp[2].fInt;
- fp = stktop - fp[1].fInt;
- if (fp == stktop)
- return nil;
- code = fp[fp[2].fInt + 3].fVec;
- cbase = (CodePtr) code->fData[0].fStr->fData;
- pc = cbase + pcoff;
- sp += n + 3;
- *sp = val;
- return pc;
- }
-
-
- // SEND opcode handler
- static CodePtr
- OpSend (CodePtr pc)
- {
- int n = *pc++;
- CheckType(n, tObject);
- CheckType(n - 1, tString);
- Value aClass = objgetclass(&sp[n]);
- TId selector;
- GetCString(selector, sizeof(selector), &sp[n-1]);
- sp[n - 1] = sp[n];
- do {
- Entry de;
- if ((de = FindEntry(clgetfunctions(aClass), selector)) != nil) {
- switch (de->fValue.fType) {
- case tCode:
- (*de->fValue.fCode)(n);
- return pc;
- case tByteCode:
- check(3);
- code = de->fValue.fVec;
- set_bytecode(&sp[n], code);
- push_integer(n); // argument count
- push_integer(stktop - fp); // old fp
- push_integer(pc - cbase); // old pc
- cbase = pc = (CodePtr) code->fData[0].fStr->fData;
- fp = sp;
- return pc;
- default:
- Error("Bad method, Selector ‘%s’, Type %d",
- selector, de->fValue.fType);
- }
- }
- aClass = clgetbase(aClass);
- } while (!isnil(aClass));
- Error("No method for selector ‘%s’", selector);
- // return nil; // Statement NOT reached
- }
-
-
- // VREF opcode handler
- static CodePtr
- OpVRef (CodePtr pc)
- {
- CheckInt0();
- switch (sp[1].fType) {
- case tVector:
- Vector vect = sp[1].fVec;
- int i = sp[0].fInt;
- if (i < 0 || i >= vect->fLength)
- Error(pSubsErr, i);
- sp[1] = vect->fData[i];
- break;
- case tString:
- String str = sp[1].fStr;
- i = sp[0].fInt;
- if (i < 0 || i >= str->fLength)
- Error(pSubsErr, i);
- set_integer(&sp[1], str->fData[i]);
- break;
- default:
- gPC = pc;
- BadType(1, tVector);
- break;
- }
- ++sp;
- return pc;
- }
-
-
- // VSET opcode handler
- static CodePtr
- OpVSet (CodePtr pc)
- {
- CheckInt1();
- switch (sp[2].fType) {
- case tVector:
- Vector vect = sp[2].fVec;
- int i = sp[1].fInt;
- if (i < 0 || i >= vect->fLength)
- Error(pSubsErr, i);
- vect->fData[i] = sp[2] = *sp;
- break;
- case tString:
- CheckInt0();
- String str = sp[2].fStr;
- i = sp[1].fInt;
- if (i < 0 || i >= str->fLength)
- Error(pSubsErr, i);
- str->fData[i] = sp[0].fInt;
- set_integer(&sp[2], str->fData[i]);
- break;
- default:
- gPC = pc;
- BadType(1, tVector);
- break;
- }
- sp += 2;
- return pc;
- }
-
-
- #define TwoTypes(a, b) (((a) << 4) + (b))
-
- // ADD opcode handler
- static CodePtr
- OpAdd (CodePtr pc)
- {
- switch (TwoTypes(sp[1].fType, sp[0].fType)) {
- case TwoTypes(tInteger, tInteger):
- sp[1].fInt += sp->fInt;
- break;
- case TwoTypes(tInteger, tString):
- String sn = NewString(1 + SLen(sp));
- String s2 = sp[0].fStr;
- sn->fData[0] = sp[1].fInt;
- memcpy(&sn->fData[1], s2->fData, s2->fLength);
- set_string(&sp[1], sn);
- break;
- case TwoTypes(tString, tInteger):
- sn = NewString(sp[1].fStr->fLength + 1);
- String s1 = sp[1].fStr;
- memcpy(sn->fData, s1->fData, s1->fLength);
- sn->fData[s1->fLength] = sp[0].fInt;
- set_string(&sp[1], sn);
- break;
- case TwoTypes(tString, tString):
- sn = NewString(sp[1].fStr->fLength + SLen(sp));
- s1 = sp[1].fStr;
- s2 = sp[0].fStr;
- memcpy(sn->fData, s1->fData, s1->fLength);
- memcpy(&sn->fData[s1->fLength], s2->fData, s2->fLength);
- set_string(&sp[1], sn);
- break;
- default:
- gPC = pc;
- BadType(1, tString);
- break;
- }
- ++sp;
- return pc;
- }
-
-
- // The type names
- static const char pTypeNames[][11] = {
- "NIL", "INTEGER", "CODE",
- "CLASS", "OBJECT", "VECTOR", "STRING",
- "BYTECODE", "DICTIONARY", "VAR", "STREAM"
- };
-
-
- // Get the name of a type
- /*static*/ KStr
- TypeName (int type)
- {
- if (type >= _tMin && type <= _tMax)
- return pTypeNames[type];
-
- static char buf[kTypeNameSize];
- sprintf(buf, "(%d)", type);
- return buf;
- }
-
-
- void
- Arg0Not (int type)
- {
- BadType(0, type);
- }
-
-
- void
- Arg0NotInt (void)
- {
- BadType(0, tInteger);
- }
-
-
- void
- Arg1NotInt (void)
- {
- BadType(1, tInteger);
- }
-
-
- // Report a bad operand type
- void
- BadType (int off, int type)
- {
- char aType[kTypeNameSize];
-
- strcpy(aType, TypeName(sp[off].fType));
- Info("PC: %04X, Offset %d, Type %s, Expected %s",
- gPC - cbase, off, aType, TypeName(type));
- Error("Bad argument type");
- }
-
-
- // Report a stack overflow error
- void
- StackOver (void)
- {
- Error("Stack overflow");
- }
-